package runload

import (
	"flag"
	. "gitee.com/kingzyt/common/behaviorTree"
	"gitee.com/kingzyt/common/log"
	"gitee.com/kingzyt/common/profile"
	. "gitee.com/kingzyt/common/util"
	"math/rand"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"
)

/*
对于可能产生的响应速度不断变慢的问题
可能的原因
当runload和servers在一台机器上，那么在runload高速运行的时候，就会跟servers抢资源，造成servers的运行效率低下，反应变慢，这样同时造成runload的res时间变长，req发送效率就降低
高磁盘占用导致文件io变慢，runload和servers都会在这个过程中写入大量log，io慢会可能拖慢整体响应
机器环境中,其他进程的高cpu占用,高内存占用,或是io频繁,都可能影响到

不会是内存，没有大规摸内存累积，长期运行，内存和cpu都没有爆掉，
不会是频繁goroutine创建和释放，不断goroutine开关的，没有影响
不会是goroutine不断累积，检测结果是稳定的，
不会是tcp状态问题，tcp的各个状态的存在数量稳定。

当一台主机做服务器(1w qps),一台跑runload(10w Robot, 1w per batch), 证明没有这个问题, 说明还是挤在一台主机上造成的互相争用
*/

type RunloadLaunchParam struct {
	LoadAllTablesFunc  func() error
	GlobalInitFunc     func() error
	GetNameListForMode func(isForNameListMode bool) []string
	GetBTRoot          func() INode
	NewRobot           func(robotId int, proxiedUserName string) IRobot
}

var defaultGetNameListForMode = func(isForNameListMode bool) (nameList []string) {
	if !isForNameListMode {
		return nil
	}

	err := IteratorFileLine("./namelist.txt", func(lineStr string) bool {
		line := strings.TrimSpace(lineStr)
		if line != "" {
			nameList = append(nameList, line)
		}
		return true
	})
	if err != nil {
		log.Error("", "read namelist file err: %s", err)
		return nil
	}
	return nameList
}

func RunloadLaunch(param RunloadLaunchParam) {
	rand.Seed(time.Now().UnixNano())

	go StartBGGoroutineStackCollect("runload")

	defer DeferRecoverPanic("runload main")

	FetchMachineLocalPs()

	flag.String("mode", "0", "robot name rule mode: idx:0 / nameList:1")
	flag.String("name", "r", "auto test Robot name prefix, for mode idx")
	flag.String("count", "100", "auto test Robot count, for mode idx")
	flag.String("offset", "0", "robot start idx offset, for mode idx")
	flag.String("eachrun", "0", "time duration(by second) which each robot will run once, 0 means running forever")
	logpath := GetExecDir() + "/log/runload"
	flag.String("logpath", logpath, "log file path")
	flag.String("logopen", "8", "open log")
	flag.String("speed", "1.0", "robot speed")
	flag.String("stat", "0", "open action statistic")
	flag.String("onebatchcount", "1000", "one batch robot max number")
	flag.String("btview", "0", "show bt state in a window")

	flag.Parse()

	profile.Start("runload", true)

	getFlagInt := func(name string) int {
		rlt, err := strconv.Atoi(flag.Lookup(name).Value.String())
		if err != nil {
			panic(name + " is not number")
		}
		return rlt
	}
	getFlagFloat := func(name string) float32 {
		rlt, err := strconv.ParseFloat(flag.Lookup(name).Value.String(), 32)
		if err != nil {
			panic(name + " is not number")
		}
		return float32(rlt)
	}
	getFlagStr := func(name string) string {
		return flag.Lookup(name).Value.String()
	}

	log.Init(log.ShowOnConsole(true))
	defer log.Flush()

	////////////////////////////////////
	if param.LoadAllTablesFunc != nil {
		err := param.LoadAllTablesFunc()
		if err != nil {
			log.Error("runload", "table load err: %s", err)
			return
		}
	}

	if param.GlobalInitFunc != nil {
		err := param.GlobalInitFunc()
		if err != nil {
			log.Error("runload", "global init err: %s", err)
			return
		}
	}

	if param.GetNameListForMode == nil {
		param.GetNameListForMode = defaultGetNameListForMode
	}

	mode := RunloaderRunningMode(getFlagInt("mode"))
	isForNameListMode := mode == Mode_NameList
	nameList := param.GetNameListForMode(isForNameListMode)
	if isForNameListMode && len(nameList) == 0 {
		log.Error("runload", "name list for NameList Mode is empty")
		return
	}

	openParams := NewRunloaderOpenParams()
	openParams.Mode = RunloaderRunningMode(getFlagInt("mode"))
	openParams.IdxNamePrefix = getFlagStr("name")
	openParams.IdxCount = getFlagInt("count")
	openParams.IdxOffset = getFlagInt("offset")
	openParams.NameList = nameList
	openParams.EachRunTS = int64(getFlagInt("eachrun"))

	speed := getFlagFloat("speed")
	if speed <= 0.1 {
		speed = 0.1
		log.Warn("runload", "speed is out of range, set to min value: %f", speed)
	} else if speed > 100.0 {
		speed = 100.0
		log.Warn("runload", "speed is out of range, set to max value: %f", speed)
	}

	if param.GetBTRoot == nil {
		log.Error("runload", "GetBTRoot is nil")
		return
	}

	BTSetWaitSpeed(speed)
	root := param.GetBTRoot()

	runloader := NewRunloader("runload", root)
	runloader.NewRobot = func(robotId int, proxiedUserName string) IRobot {
		robot := param.NewRobot(robotId, proxiedUserName)
		//robot.GetRuntimeNodeStack().TestShow(true)

		/*if getFlagStr("btview") == "1" && robotId == 0 {
			go NewBTViewer("runload", root, robot).Run(2048, 1024)
		}*/

		return robot
	}

	runloader.Open(getFlagInt("onebatchcount"), time.Duration(int64(float32(int64(time.Second*3))/speed)), openParams)
	if !runloader.Run() {
		log.Error("runload", "runload run fail")
		return
	}

	if getFlagInt("stat") != 0 {
		UpdateProc(time.Second*10, false, func() (ok bool) {
			ShowActionTotalStats("", runloader.GetActionsStat())
			return true
		})
	}

	osc := make(chan os.Signal, 1)
	signal.Notify(osc, os.Kill, os.Interrupt, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGINT)
	<-osc

	runloader.Quit()

	profile.Stop()
	log.Info("runload", log.I, "runload stopped [OK]")
}
